Modul Autentikimi
Te gjitheve, te pakten nje here ne jete, na eshte dashur te futim nje email dhe fjalekalim, ose nje emer perdoruesi (ang. username) dhe fjalekalim per te hyre brenda nje aplikacioni, ose me sakte per te hyre ne llogarine (ang. account) tone personale. Tashme termat emer perdoruesi dhe fjalekalim jemi mesuar qe t'i perdorim username dhe password dhe na duket sikur jane pjese e gjuhes shqipe, per shkak se jane te mbiperdorura si terma. Edhe kur ndezim kompjuterin tone na kerkohet te futim nje username dhe password. Keto te dhena i quajme ndryshe kredenciale. Kredencialet jane informacione personale qe me ane te tyre njihemi ne sisteme te ndryshme qe jemi vertete ne qe kerkojme te aksesojme aplikacionin ose pjese te caktuara te tij.
Natyrisht, te gjithe mund te kemi perdorur rrjete sociale si facebook, twitter, linkedin etj. Secili prej tyre ka pjese te vecanta qe mund te aksesohen pa futur kredencialet p.sh. nese vizitojme faqen facebook.com do te hapet nje faqe mireseardhjeje e cila na fton te regjistrohemi qe te krijojme llogari personale ose te logohemi. Ne rast se nuk futim kredencialet nuk mund te shohim faqe te tjera sic eshte profili, fotografite, postimet etj. qe mund t'i quajme faqe te mbrojtura. Detyrimisht nese duam te aksesojme keto faqe duhet te futim kredencialet personale ose ndryshe te autentikohemi.
Ne kete seksion do te paraqesim nje menyre sesi mund te ndertojme nje modul autentikimi. Autentikimi i nje perdoruesi ne nje sistem kalon ne disa faza te listuara me poshte:
Fillimisht perdoruesit te sistemit i shfaqet nderfaqja per t'u autentikuar e cila kerkon nje email (ose username) dhe password. Qe nje perdorues te autentikohet ai fillimisht duhet te jete i regjistruar ne sistem.
Perdoruesi fut kredencialet (email dhe password) ne formen e logimit dhe klikon mbi butonin
Hyr
(ang. Log in ose Sign in).- Sistemi i merr kredencialet dhe verifikon n.q.s. ato jane te sakta ose jo. Behet nje kontroll ne bazen e te dhenave nese ekziston nje perdorues me kredenciale te tilla.
- Ne rast se nuk ekziston perdorues me keto kredenciale, kthehet nje pergjigje negative me nje mesazh te tipit
Kredenciale te gabuara!
- Ne rast se kredencialet jane te sakta, pra perdoruesi ekziston, te dhenat e ketij perdoruesi ruhen ne nje sesion ne server qe do te na sherbejne gjate kohes qe perdoruesi navigon ne faqet e mbrojtura.
- Pasi ruhet sesioni, perdoruesi ridrejtohet tek nje faqe e brendshme e aplikacionit si p.sh. profili i tij.
- Per te aksesuar faqet e tjera te mbrojtura, perdoruesi nuk ka nevoje te fute kredenciale, pasi kredencialet futen vetem nje here ne fillim.
Gjate kohes qe perdoruesi navigon ne faqet e tjera te mbrojtura, behet nje kontroll i vazhdueshem nese eshte vendosur sesion apo jo. Dukeqenese sesioni eshte vendosur, perdoruesi eshte i lire te navigoje ne to.
Ne fund, perdoruesi duhet te dale nga sistemi duke klikuar ne nje buton ose link te quajtur
Dil
(ang. Log out). Ne kete moment sesioni shkaterrohet dhe perdoruesi ridrejtohet tek nje faqe e jashtme e aplikacionit e cila nuk eshte e mbrojtur. Per te hyre serish perdoruesi duhet te fute kredencialet.
Kuptohet qarte qe per te ndertuar nje modul autentikimi do te shfrytezojme sesionet e serverit, i cili i ruan keto te dhena deri ne momentin qe perdoruesi e shkeput lidhjen totalisht, p.sh. duke mbyllur brouserin, ose duke u larguar nga aplikacioni. Sesionet i kemi trajtuar edhe me pare duke qartesuar qe per t'i perdorur ato fillimisht duhen nisur duke therritur funksionin session_start()
. Me pas mund te perdorim sesionet sipas deshires si ne shembujt e meposhtem:
<?php
// Nisim sesionet:
session_start();
// Krijojme nje sesion:
$_SESSION['emri_sesionit'] = "Sesion qe mban nje stringe.";
// Fshijme nje sesion:
unset($_SESSION['emri_sesionit']);
// Shkaterrojme te gjitha sesionet:
session_destroy();
Per nje menaxhim me te mire te sesioneve, do te krijojme nje klase te pershtatur me disa metoda statike te cilat do te jene te afta te nisin, te vendosin, te fshijne dhe te shkaterrojne sesionet. Kete klase do t'a perdorim per te ndertuar modulin e autentikimit.
<?php
class Session {
public static function start()
{
session_start();
}
public static function set($key, $value)
{
$_SESSION[$key] = $value;
}
public static function get($key)
{
if (isset($_SESSION[$key]))
return $_SESSION[$key];
else
return null;
}
public static function clear($key)
{
unset($_SESSION[$key]);
}
public static function destroy()
{
session_destroy();
}
}
Tashme me ane te kesaj klase, mund te kryejme veprimet e meposhtme:
<?php
// Nisim sesionet:
Session::start();
// Krijojme nje sesion:
Session::set("emri_sesionit", "Sesion qe mban nje stringe.");
// Fshijme nje sesion:
Session::clear("emri_sesionit");
// Shkaterrojme te gjitha sesionet:
Session::destroy();
Per te ndertuar nje modul autentikimi fillimisht na duhet nje nderfaqe HTML, e cila i jep mundesi vizitorit te fute email, password dhe te klikoje mbi nje buton per te hyre. Me poshte shfaqim nje faqe me emrin login.php
e cila permban HTML-ne e nevojshme per formen e autentikimit.
<!-- Faqja login.php -->
<!DOCTYPE html>
<html>
<head>
<title>Modul Autentikimi</title>
</head>
<body>
<div class="login-page">
<!-- Ne rast se kredencialet jane te gabuara ketu duhet te shfaqet nje mesazh gabimi! -->
<div class="form">
<form method="POST">
<input type="text" name="email" placeholder="E-mail"/>
<input type="password" name="password" placeholder="Password"/>
<button name="login">Log in</button>
</form>
</div>
</div>
</body>
</html>
Kur nje perdorues fut kredencialet dhe klikon butonin Log in
, sistemi duhet te verifikoje nese te dhenat jane te sakta duke kontrolluar ne bazen e te dhenave. Do te perdorim klasen Database
te shpjeguar ne seksionet e meparshme per te bere komunikimin dhe veprime te ndryshme ne databaze. Fillimisht krijojme nje file me emrin AuthUser.php
dhe ne brendesi te saj krijojme klasen AuthUser
. Kjo klase do te permbaje nje metode te quajtur authenticate(...)
e cila do te marre ne hyrje kredencialet e perdoruesit, dhe do te na ktheje ne dalje FALSE
nqs. kredencialet jane te gabuara. Ne rast se kredencialet jane te sakta, do te na ktheje te dhenat e perdoruesit, ose me sakte do te na ktheje nje objekt te modelit Perdorues
te cilin do t'a implementojme me poshte.
Krijojme nje model me emrin Perdorues
, qe do te perfaqesoje klasen perdoruesit
:
<?php
// Modeli Perdorues:
//Importimi i skedareve te nevojshem:
require_once 'BaseModel.php';
class Perdorues extends BaseModel {
private $id;
public $emri;
public $email;
public $password;
public $tipi = 1;
public $id_departament;
function __construct(array $user = []) {
parent::__construct();
$this->emri = isset($user['emri']) ? $user['emri'] : null;
$this->email = isset($user['email']) ? $user['email'] : null;
$this->password = isset($user['password']) ? $user['password'] : null;
$this->tipi = isset($user['tipi']) ? $user['tipi'] : null;
$this->id_departament = isset($user['id_departament']) ? $user['id_departament'] : null;
}
public function getId() {
return $this->id;
}
public function save() {
if(is_null($this->id)) {
$new_id = $this->db->insert("perdoruesit", [
"emri" => $this->emri,
"email" => $this->email,
"password" => $this->password,
"tipi" => $this->tipi,
"id_departament" => $this->id_departament
]);
return $new_id;
} else {
$rezultati = $this->db->update("perdoruesit", [
"emri" => $this->emri,
"email" => $this->email,
"password" => $this->password,
"tipi" => $this->tipi,
"id_departament" => $this->id_departament
], "id = {$this->id}");
return $rezultati;
}
}
public function delete() {
$rezultati = $this->db->delete("perdoruesit", "id = {$this->id}");
return $rezultati;
}
public static function getById(int $id) {
$sql = "SELECT * FROM perdoruesit WHERE id = :id";
$db = new Database();
$rekord = $db->select($sql, [":id" => $id]);
// $rekord eshte nje array me listen e rekordeve qe u kthye
if(count($rekord)) {
// Krijojme nje objekt perfaqesues per rekordin qe u kthye nga databaza
$perdorues = new Perdorues();
$perdorues->id = $rekord[0]["id"];
$perdorues->emri = $rekord[0]["emri"];
$perdorues->email = $rekord[0]["email"];
$perdorues->password = $rekord[0]["password"];
$perdorues->tipi = $rekord[0]["tipi"];
$perdorues->id_departament = $rekord[0]["id_departament"];
return $perdorues;
} else {
return null;
}
}
public static function getList(string $condition = "1") {
$sql = "SELECT * FROM perdoruesit WHERE $condition";
$db = new Database();
$rekordet = $db->select($sql);
// $rekordet eshte nje array me listen e rekordeve qe u kthye
$perdoruesit = []; //Ky array do te mbaje listen e objekteve te tipit `Perdorues`
if(count($rekordet)) {
foreach($rekordet as $rekord) {
//Per cdo rekord te kthyer nga databaza krijojme nje objekt:
$perdorues = new Perdorues();
$perdorues->id = $rekord["id"];
$perdorues->emri = $rekord["emri"];
$perdorues->email = $rekord["email"];
$perdorues->password = $rekord["password"];
$perdorues->tipi = $rekord["tipi"];
$perdorues->id_departament = $rekord["id_departament"];
// E shtojme objektin e krijuar ne array-n kryesor:
array_push($perdoruesit, $perdorues);
}
//Kthejme listen e objekteve te tipit `Perdorues`:
return $perdoruesit;
} else {
return array();
}
}
}
Pasi krijuam modelin e mesiperm, mund t'a perdorim per te shtuar nje rekord ne databaze, i cili do te na sherbeje per te bere teste me autentikimin.
<?php
$perdorues = new Perdorues();
$perdorues->emri = "Alban Afmeti";
$perdorues->email = "[email protected]";
$perdorues->password = "test";
$perdorues->tipi = 1; //Tipi: Pedagog
$perdorues->id_departament = 1;
$perdorues->save();
Pas ekzekutimit te kodit te mesiperm, nese bejme nje SELECT
ne databaze do te shohim qe ne tabelen perdoruesit
eshte shtuar nje rekord:
Komanda:
SELECT * FROM perdoruesit;
id | emri | password | tipi | id_departament | |
---|---|---|---|---|---|
1 | Alban Afmeti | [email protected] | test | 1 | 1 |
Krijojme skedarin AuthUser.php
me permbajtjen e meposhtme:
<?php
require_once "Perdorues.php";
require_once "Session.php";
class AuthUser {
public static function authenticate(string $email, string $password) {
$sql = "SELECT * FROM perdoruesit WHERE email = :email AND password = :password";
$db = new Database();
$rezultati = $db->select($sql, [
":email" => $email,
":password" => $password
]);
if(count( $rezultati) ) {
$perdorues = new Perdorues([
"emri" => $rezultati[0]["emri"],
"email" => $rezultati[0]["email"],
"tipi" => $rezultati[0]["tipi"],
"id_departament" => $rezultati[0]["id_departament"],
]);
return $perdorues;
} else {
return false;
}
}
public static function save(Perdorues $perdorues) {
Session::set("auth", $perdorues);
}
public static function is_logged() {
if(is_null(Session::get("auth"))) {
return false;
} else {
return true;
}
}
public static function logout() {
Session::clear("auth");
}
public function get() {
return Session::get("auth");
}
}
Klasa AuthUser
permban disa metoda te rendesishme:
authenticate(...)
- Verifikon kredencialet e dhena si argument dhe kthenFALSE
nqs. kredencialet jane te gabuara, ose kthen nje objekt te klasesPerdorues
nqs. kredencialet jane te sakta.save(...)
- Pas verifikimit te kredencialeve, per te ruajtur nje sesion ne server (i cili verteton qe perdoruesi eshte i loguar ne sistem) therrasim metodensave(...)
. Ajo cfare ne te vertete behet eshte ruajtja e nje sesioni me celesauth
. Duhet te kemi kujdes pasi ky celes ne sesion nuk duhet te mbishkruhet nga pjesa e tjeter e aplikacionit.is_logged()
- Metoda verifikon nese nje ekziston nje perdorues i loguar ne faqe apo jo.logout()
- Pastron sesionin me celesauth
ne menyre qe nese perdoruesi do te aksesoje nje faqe te mbrojtur, duhet te logohet perseri.get()
- Kthen nje objekt te klasesPerdorues
, i cili mban te dhenat e perdoruesit te loguar. Ne rast se perdoruesi nuk eshte i loguar kthennull
.
Me poshte do t'i shohim keto metoda ne perdorim.
Fillimisht, supozojme qe pervec faqes login.php
ekziston dhe nje faqe tjeter profile.php
e cila eshte e mbrojtur, pra aksesohet vetem ne rast se perdoruesi eshte i loguar. Kodi i saj paraqitet me poshte:
<?php
// Faqja profile.php
require_once "AuthUser.php";
if(!AuthUser::is_logged()) {
// Nqs. perdoruesi nuk eshte i loguar, e ridrejtojme tek faqja login.php qe te logohet
//Pra, nuk e lejojme te vazhdoje me hapjen e kesaj faqeje
header("Location: login.php");
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Profili i Perdoruesit</title>
</head>
<body>
<div class="user-profile">
</div>
</body>
</html>
Faqen e mesiperme e konsiderojme faqe te mbrojtur, pasi nuk lejohet aksesimi saj nese nuk je i loguar.
Le te vazhdojme me implementimin e faqes login.php
qe te jete funksionale. Ne momentin qe perdoruesi do te klikoje mbi butonin Log in
do te dergohet nje kerkese POST
ne server sebashku me kredencialet qe ka futur perdoruesi. Dukeqenese tek forma e logimit nuk kemi deklaruar nje atribut action
, kerkesa POST
do te dergohet mbi te njejtin skedar. Pra, edhe trajtimi i saj duhet te behet ne skedarin login.php
. Keshtu qe pervec pjeses HTML, ne krye te faqes login.php
shtojme kodin e meposhtem:
<?php
// Faqja login.php
require_once "AuthUser.php";
if(isset($_POST["login"])) {
$email = $_POST["email"];
$password = $_POST["password"];
$result = AuthUser::authenticate($email, $password);
if($result === false) {
// Kredencialet jane te gabuara
} else {
// Kredencialet jane te sakta
}
}
?>
<!-- Vazhdon kodi HTML -->
...
Pasi perdoruesi ben submit formen e logimit, mund te aksesojme emailin dhe passwordin qe ka dhene nepermjet $_POST["email"]
dhe $_POST["password"]
, pasi kerkesa qe u dergua ne server ishte e tipit POST
. Kontrolli if(isset($_POST["login"]))
kontrollon nqs. eshte bere forma submit. Arsyeja pse behet ky kontroll eshte se kodi i mesiperm i PHP-se do te ekzekutohet edhe kur hapet faqja login.php
per here te pare, p.sh. kur kerkojme ne browser www.shembull.com/login.php
ku shembull.com
eshte domeini i serverit ku kemi hostuar skedaret. Ndaj duhet te bejme te mundur qe te trajtojme kredencialet e perdoruesit vetem kur forma behet submit. Me ane te AuthUser::authenticate($email, $password);
verifikojme kredencialet dhe do te na kthehet false
ne rast se jane te gabuara, perndryshe do te na kthehet nje objekt i tipit Perdorues
me te dhenat e perdoruesit qe po logohet.
Vazhdojme me implementimin:
<?php
// Faqja login.php
require_once "AuthUser.php";
if(isset($_POST["login"])) {
$email = $_POST["email"];
$password = $_POST["password"];
$result = AuthUser::authenticate($email, $password);
if($result === false) {
$error_msg = "Kredenciale te gabuara.";
} else {
AuthUser::save($result);
header("Location: profile.php");
}
}
?>
<!-- Vazhdon kodi HTML -->
...
Nese kredencialet jane te sakta ruajme sesionin e perdoruesit te loguar duke therritur AuthUser::save($result);
, dhe me pas ridrejtohemi tek faqja profile.php
duke perdorur funksionin header(...)
. Dukeqenese po ruajme sesionin e perdoruesit te loguar, faqja profile.php
do te jete e dukshme sepse deklarata AuthUser::is_logged()
e cila mbron faqen profile.php
do te na ktheje true
.
Ne rast se kredencialet jane te gabuara, krijojme nje variabel $error_msg
qe mban tekstin Kredenciale te gabuara.
. Kete tekst duhet ta renderizojme ne HTML qe t'i shfaqet mesazhi perdoruesit ne brouser, ndaj bejme pak ndryshime ne kodin HTML si me poshte:
<!-- ... -->
<!-- Kodi i mesiperm PHP -->
<!DOCTYPE html>
<html>
<head>
<title>Modul Autentikimi</title>
</head>
<body>
<div class="login-page">
<!-- Ne rast se kredencialet jane te gabuara ketu duhet te shfaqet nje mesazh gabimi! -->
<?php
if(isset($error_msg)) {
echo "<p class='error'> {$error_msg} </p>";
}
?>
<div class="form">
<form method="POST">
<input type="text" name="email" placeholder="E-mail"/>
<input type="password" name="password" placeholder="Password"/>
<button name="login">Log in</button>
</form>
</div>
</div>
</body>
</html>
Ne momentin qe perdoruesi logohet ne aplikacion, ne mund ti aksesojme te dhenat e tij nepermjet funksionit AuthUser::get()
.
<?php
if(AuthUser::is_logged()) {
$perdorues = AuthUser::get();
echo $perdorues->emri; //Rezultati: Alban Afmeti
}